package com.pijunn.label

import grails.converters.JSON
import grails.validation.Validateable;

import org.springframework.dao.DataIntegrityViolationException
import org.springframework.validation.FieldError

import com.pijunn.common.enums.AveryLabelSize;
import com.pijunn.common.enums.LabelType;
import com.pijunn.users.AppUser;

class LabeledItemController {

    static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
	/**
	 * Dependency injection for the springSecurityService.
	 */
	def springSecurityService
	
	def labelService

    def index() {
        redirect(action: "list", params: params)
    }

    def list(Integer max) {
        params.max = Math.min(max ?: 10, 100)
		AppUser user = springSecurityService.currentUser
		def admin = principal.authorities*.authority.contains("ROLE_ADMIN")

		def list = admin ? LabeledItem.list(params) : labelService.getMyItems()
		def count = admin ? LabeledItem.count() : labelService.getMyItems().size()
		
		log.info '*** List size: ' + count
		
        [labeledItemInstanceList: list, labeledItemInstanceTotal: count]
    }

    def create() {
		AppUser user = springSecurityService.getCurrentUser()
		def itemList = []
		for(label in user.assignedTags.sort{ it.serialNumber }) {
			if (!label.item)
				itemList.push(new LabeledItem([label: label]))
		}

		[labelList: itemList, templateLabel: [label: new Label([assigned: false])], user: user ]
    }

    def save = { CreateItemsCommand cmd ->
		
		if (cmd.hasErrors()) {
            render(view: params['view'] ?: "create", model: [cmd: cmd, labelList: cmd.labelList(), user: principal])
            return
		}

		def labels = cmd.labelList().findAll { it.name.trim() != '' && it.description.trim() != '' }
		if (labels.size() > 0)
			for (i in 0..(labels.size()-1)) {
				def version = cmd.version[i]
		        if (version != null) {
		            if (labels[i].version > version) {
		                //labels[i].errors.rejectValue("version", "default.optimistic.locking.failure",
		                //          [message(code: 'labeledItem.label', default: 'Labeled Item')] as Object[],
		                //          "Another user has updated this LabeledItem while you were editing")
						flash.message = "${message(code: 'labeledItem.label', default: 'Another user has updated this LabeledItem while you were editing')}"
		                render(view: "edit", model: [cmd: cmd, labelList: labels])
		                return
		            }
		        }
			}
		
        if (!labelService.saveLabels(labels)) {
            render(view: params['view'] ?: "create", model: [cmd: cmd, labelList: cmd.labelList()])
            return
	    }

        flash.message = message(code: params['view'] == 'edit' ? 'default.updated.message' : 'default.created.message', args: [labels.size(), message(code: 'labeledItem.label', default: 'Labeled Items')])
        redirect(action: "list", id: 10)
    }

    def show(Long id) {
        def labeledItemInstance = LabeledItem.get(id)
        if (!labeledItemInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'labeledItem.label', default: 'LabeledItem'), id])
            redirect(action: "list")
            return
        }

        [labeledItemInstance: labeledItemInstance]
    }

    def edit(Long id) {
		if (id) {
	        def labeledItemInstance = LabeledItem.get(id)
	        if (!labeledItemInstance) {
	            flash.message = message(code: 'default.not.found.message', args: [message(code: 'labeledItem.label', default: 'LabeledItem'), id])
	            redirect(action: "list")
	            return
	        }
			
			def labelList = [labeledItemInstance.label]
			labelList.addAll(labelService.getMyAvailableLabels())
	
	        [labeledItemInstance: labeledItemInstance, myUnassignedLabels: labelList]
		}
		else {
			AppUser user = springSecurityService.getCurrentUser()
			def itemList = []
			def cmd = new CreateItemsCommand()
			for(label in user.assignedTags.sort{ it.serialNumber }) {
				cmd.confirmSerial.push((int)label.serialNumber)
				for (item in label.item) {
					itemList.push(item)
				}
			}
			[labelList: itemList, templateLabel: [label: new Label([assigned: false]), cmd: cmd] ]
		}
    }

    def update(Long id, Long version) {
        def labeledItemInstance = LabeledItem.get(id)
        if (!labeledItemInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'labeledItem.label', default: 'LabeledItem'), id])
            redirect(action: "list")
            return
        }

        if (version != null) {
            if (labeledItemInstance.version > version) {
                labeledItemInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
                          [message(code: 'labeledItem.label', default: 'LabeledItem')] as Object[],
                          "Another user has updated this LabeledItem while you were editing")
                render(view: "edit", model: [labeledItemInstance: labeledItemInstance])
                return
            }
        }

        labeledItemInstance.properties = params

        if (!labeledItemInstance.save(flush: true)) {
            render(view: "edit", model: [labeledItemInstance: labeledItemInstance])
            return
        }

        flash.message = message(code: 'default.updated.message', args: [message(code: 'labeledItem.label', default: 'LabeledItem'), labeledItemInstance.id])
        redirect(action: "show", id: labeledItemInstance.id)
    }

    def delete(Long id) {
 		def item = LabeledItem.get(id)
		 
		 if (!item) {
			 flash.message = message(code: 'default.not.found.message', args: [message(code: 'labeledItem.label', default: 'Labeled Item'), id])
			 redirect(action: "list")
			 return
		 }
 
		if (labelService.removeItem(item)) {
			flash.message = message(code: 'default.deleted.message', args: [message(code: 'labeledItem.label', default: 'Labeled Item'), id])
			redirect(action: "list")
		}
		else {
			flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'labeledItem.label', default: 'Labeled Item'), id])
			redirect(action: "show", id: id)
		}
    }
	
	def assign = { AssignLabelsCommand cmd ->
		if (request.method == 'POST') {
			if (cmd.validate()) {
				Integer currentLabelSerial = params.int('startSerialNumber'),
						end = params.int('endSerialNumber')
				
				AppUser user = AppUser.get(params.long('user.id'))
				while(currentLabelSerial <= end) {
					def label = Label.findBySerialNumber(currentLabelSerial)
					label.registeredTo = user
					label.assigned = true;
					if (label.validate()) {
						log.debugEnabled ? log.debug('Updating label with id: ' + currentLabelSerial) : ''
						label.save(flush:true)
					}
					else if(!cmd.errors.hasErrors()){
						def labelMessage
						label.errors.allErrors.each {FieldError error ->
							final String field = error.field?.replace('label.', '')
							final String code = "generateLabelsCommand.$field.$error.code"
							log.debug(error)
							cmd.errors.rejectValue("startSerialNumber", code, [label.serialNumber.toString()].toArray(), 'Error saving label')
						}
						break;
					}
					currentLabelSerial++
				}
				
				if (cmd.errors.hasErrors()) {
					flash.message = 'A problem occurred while assigning your labels.  Please enter them manually.'
					render(view:'create', model:[templateLabel: [label: new Label([assigned: false])], user: springSecurityService.getCurrentUser()])
				} else {
					flash.message = message(code: 'default.updated.message', args: [(params.int('endSerialNumber') - params.int('startSerialNumber') + 1), message(code: 'label.label', default: 'Inventory Labels')])
					redirect(action: "create")
				}
		
			}
			else {
				flash.message = cmd.errors?.allErrors?.collect{ message(error: it) }
				render(view:'create', model:[templateLabel: [label: new Label([assigned: false])], user: springSecurityService.getCurrentUser()])
			}
		}

	}
	
	def notMine(Integer id) {
		Label label = Label.get(id)
		
		if (label) {
			//label.item[0].label = null
			//label.item[0].save()
			
			label.assigned = false
			label.registeredTo = null
			label.item = null
			label.save()
		}
		
		render label as JSON
	}

	def generate() {
		def labels = Label.findAllByTypeAndRegisteredToIsNullAndAssigned(LabelType.Avery.name(), false, [max: 10, sort: 'serialNumber'])
		
		if (labels?.size() == 0) {
			flash('Sorry, there are no more Labels available for printing')
			render(action: 'create')
		}
		
		for (label in labels) {
			label.assigned = true
			label.registeredTo = springSecurityService.currentUser
			label.save(flush: true)
		}
		
		redirect(action: 'print')
	}
	

	def print() {
		['labels': Label.findAllByRegisteredTo(springSecurityService.currentUser), 'sizes':AveryLabelSize.values()]
	}
}

class CreateItemsCommand {
	List<Integer> itemId = []
	List<Integer> labelId = []
	List<Integer> serialNumber = []
	List<Integer> confirmSerial = []
	List<String> name = []
	List<String> description = []
	List<String> deleted = []
	List<Long> version = []
	List<Boolean> regLabel = []
	def view
	
	List labelList() {
		List<LabeledItem> list = []
		def i = 0
		for (name in this.name) {
			def label 
			if (serialNumber[i])
				label = Label.findBySerialNumber(serialNumber[i])
			if (!label)
				label = new Label([serialNumber: serialNumber[i], assigned: false, id: labelId[i]])
				
			def item = new LabeledItem()
			if (itemId[i]) {
				item = LabeledItem.get( itemId[i] )
			}
			item.name = name
			item.description = description[i]
			item.label = label
			list.push(item)
			i++
		}
		
		list
	}

	
	static constraints = {
		name validator: {val, form, err ->
			def error = true
			for (i in 0..(form.deleted.size()-1 == -1 ? 0 : form.deleted.size()-1)){
				// Allow empty items
				if (form.name[i]?.trim() == '' && form.description[i]?.trim() == '')
					continue;
					
				if (form.name[i]?.trim() == '') {
					err.rejectValue("name[${i}]", 'labeledItem.name.blank.message')
					error = false
				}
				
				if (!form.description[i] || form.description[i]?.trim() == '') {
					err.rejectValue("description[${i}]", 'labeledItem.description.blank.message')
					error = false
				}
				
				if (form.serialNumber[i] == null) {
					err.rejectValue("serialNumber[${i}]", 'labeledItem.serialNumber.blank.message')
					error = false
				}
				else if (form.confirmSerial[i] == null) {
					err.rejectValue("confirmSerial[${i}]", 'labeledItem.confirmSerialNumber.blank.message')
					error = false
				}
				else {
					if (form.serialNumber[i] < 10000000 || form.serialNumber[i] > 99999999) {
						err.rejectValue("serialNumber[${i}]", 'labeledItem.serialNumber.invalid.size')
						error = false
					}
					if (form.confirmSerial[i] < 10000000 || form.confirmSerial[i] > 99999999) {
						err.rejectValue("confirmSerial[${i}]", 'labeledItem.confirmSerialNumber.invalid.size')
						error = false
					}
					if(form.serialNumber[i] != form.confirmSerial[i]) {
						err.rejectValue("confirmSerial[${i}]", 'labeledItem.serialNumber.not.confirmed')
						error = false
					}
					else if(!form.regLabel[i]){
						def label = Label.findBySerialNumber(form.serialNumber[i])
						if (!label) {
							err.rejectValue("serialNumber[${i}]", 'labeledItem.serialNumber.not.exist')
							error = false
						}
						else if(label.registeredTo != null && !form.labelId[i]) { // Only perform on add
							err.rejectValue("serialNumber[${i}]", 'labeledItem.serialNumber.owned')
							error = false
						}
						else {
							form.labelId[i] = label.id
						}
					}
				}
			}
			return error
		}
	}
}

